package com.shuwen.gcdj.common.util;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.PropertyUtilsBean;
import org.apache.commons.lang.StringUtils;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;

@Slf4j
public class BeanUtil {

    private BeanUtil() {
    }

    /**
     * 获取集合类的String 属性 的value集合
     *
     * @param list  bean 集合
     * @param field 属性名称
     * @return
     */
    public static <T> Set<String> getStringFields(Collection<T> list, String field) {
        Set<String> fields = new HashSet<>();
        Field fieldF = null;
        for (T t : list) {
            try {
                fieldF = t.getClass().getDeclaredField(field);
                fieldF.setAccessible(true);
                if (fieldF.get(t) != null && StringUtils.isNotBlank(fieldF.get(t).toString())) {
                    fields.add(fieldF.get(t).toString());
                }
            } catch (Exception e) {
                log.error("get field value error", e);
            } finally {
                if(fieldF != null){
                    fieldF.setAccessible(false);
                }
            }
        }
        return fields;
    }

    /**
     * 获取集合类的String 属性 的value集合
     *
     * @param list  bean 集合
     * @param field1 属性名称1
     * @param field2 属性名称2
     * @return
     */
    public static <T> Set<String> getStringFields(Collection<T> list, String field1, String field2) {
        Set<String> fields = new HashSet<>();
        Field fieldF1 = null;
        Field fieldF2 = null;
        for (T t : list) {
            try {
                fieldF1 = t.getClass().getDeclaredField(field1);
                fieldF2 = t.getClass().getDeclaredField(field2);
                fieldF1.setAccessible(true);
                fieldF2.setAccessible(true);
                if (fieldF1.get(t) != null && StringUtils.isNotBlank(fieldF1.get(t).toString()) &&
                        fieldF2.get(t) != null && StringUtils.isNotBlank(fieldF2.get(t).toString())) {
                    fields.add(fieldF1.get(t).toString() + fieldF2.get(t).toString());
                }
            } catch (Exception e) {
                log.error("get field value error", e);
            } finally {
                if(fieldF1 != null){
                    fieldF1.setAccessible(false);
                }
                if(fieldF2 != null){
                    fieldF2.setAccessible(false);
                }
            }
        }
        return fields;
    }

    /**
     * 使用Introspector，map集合成javabean
     *
     * @param map       map
     * @param beanClass bean的Class类
     * @return bean对象
     */
    public static <T> T mapToBean(Map<String, Object> map, Class<T> beanClass) {
        if (MapUtil.isEmpty(map)) {
            return null;
        }
        try {
            T t = beanClass.newInstance();
            BeanInfo beanInfo = Introspector.getBeanInfo(t.getClass());
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor property : propertyDescriptors) {
                Method setter = property.getWriteMethod();
                if (setter != null && map.get(property.getName()) != null) {
                    setter.invoke(t, map.get(property.getName()));
                }
            }
            return t;
        } catch (Exception ex) {
            log.error("########map集合转javabean出错######，错误信息，{}", ex.getMessage());
            throw new ClassCastException();
        }

    }

    /**
     * 通过反射获取对象中的值
     *
     * @param bean
     * @param fieldName
     * @param <T>
     * @return
     */
    public static <T> Object getFieldValue(T bean, String fieldName) {
        if (bean == null || StringUtils.isBlank(fieldName)) {
            return null;
        }
        Field field = null;
        try {
            field = bean.getClass().getDeclaredField(fieldName);
        } catch (NoSuchFieldException e) {
            log.error("getFieldValue error: ", e);
        }
        if (field == null) {
            return null;
        }
        field.setAccessible(true);
        Object value = null;
        try {
            value = field.get(bean);
        } catch (IllegalAccessException e) {
            log.error("getFieldValue error: ", e);
        } finally {
            field.setAccessible(false);
        }
        return value;
    }

    /**
     * 通过反射设置对象中的值
     *
     * @param bean
     * @param fieldName
     * @param <T>
     * @return
     */
    public static <T> void setFieldValue(T bean, String fieldName, Object value) {
        if (bean == null || StringUtils.isBlank(fieldName)) {
            return;
        }
        Field field = searchDeclaredFieldByName(bean, fieldName);

        if (field == null) {
            return;
        }

        field.setAccessible(true);

        try {
            field.set(bean, value);
        } catch (IllegalAccessException e) {
            log.error("setFieldValue error: ", e);
        } finally {
            field.setAccessible(false);
        }
    }

    /**
     * 从bean中找到指定字段
     *
     * @param bean
     * @param fieldName
     * @param <T>
     * @return
     */
    private static <T> Field searchDeclaredFieldByName(T bean, String fieldName) {
        if (bean == null || StringUtils.isBlank(fieldName)) {
            return null;
        }

        Field[] fields = bean.getClass().getDeclaredFields();

        return Arrays.stream(fields).filter(field -> fieldName.equals(field.getName())).findFirst().orElse(null);
    }

    /**
     * 通过反射设置对象中的值（批量）
     *
     * @param bean
     * @param map
     * @param <T>
     */
    public static <T> void setFieldValues(T bean, Map<String, ? extends Object> map) {
        if (MapUtil.isEmpty(map)) {
            return;
        }
        map.entrySet().forEach(item -> setFieldValue(bean, item.getKey(), item.getValue()));
    }

    /**
     * 生成单个键值对的map
     *
     * @param key
     * @param value
     * @return
     */
    public static <T> Map<String, T> createMap(String key, T value) {
        Map<String, T> map = CollectionUtil.newHashMap();
        map.put(key, value);
        return map;
    }

    /**
     * 将多个collection合并
     *
     * @param collections
     * @param <T>
     * @return
     */
    @SafeVarargs
    public static <T> Collection<T> allCollectionAll(Collection<T>... collections) {
        if (collections == null || collections.length == 0) {
            return new ArrayList<>();
        }
        Collection<T> collection = new ArrayList<>();
        for (Collection<T> c : collections) {
            if (CollectionUtil.isNotEmpty(c)) {
                collection.addAll(c);
            }
        }
        return collection;
    }

    /**
     * 按某个属性去重
     *
     * @param keyExtractor
     * @param <T>
     * @return
     */
    public static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
        Map<Object, Boolean> seen = new ConcurrentHashMap<>();
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }

    /**
     * 将javabean实体类转为map类型，然后返回一个map类型的值
     *
     * @param objs
     * @param <T>
     * @return
     */
    public static <T> List<Map<String, Object>> beanListToMapList(List<T> objs) {
        List<Map<String, Object>> params = new ArrayList<>();
        if (objs == null || objs.isEmpty()) {
            return params;
        }
        try {
            PropertyUtilsBean propertyUtilsBean = new PropertyUtilsBean();
            PropertyDescriptor[] descriptors = propertyUtilsBean.getPropertyDescriptors(objs.get(0));
            for (T obj : objs) {
                if (null == obj) {
                    continue;
                }
                Map<String, Object> param = new HashMap<>();
                for (int i = 0; i < descriptors.length; i++) {
                    String name = descriptors[i].getName();
                    if (!"class".equals(name)) {
                        param.put(name, propertyUtilsBean.getNestedProperty(obj, name));
                    }
                }
                params.add(param);
            }
        } catch (Exception e) {
            log.error("beanListToMapList,error:", e);
        }
        return params;
    }

    /**
     * 复制对象到另一个类型的对象中
     *
     * @param source
     * @param targetClass
     * @param <S>
     * @param <T>
     */
    public static <S, T> T convertBean(S source, Class<T> targetClass) {
        if (source == null || targetClass == null) {
            return null;
        }
        T target = null;
        try {
            target = targetClass.newInstance();
            BeanHelper.copyProperties(source, target);
            return target;
        } catch (InstantiationException | IllegalAccessException e) {
            log.error("convertBean error:", e);
        }
        return target;
    }
}
